fix(llmobs): filter openai Omit/NotGiven sentinels from span metadata#18552
Conversation
|
Codeowners resolved as |
f04d054 to
df1efab
Compare
emmettbutler
left a comment
There was a problem hiding this comment.
release note looks fine
Yun-Kim
left a comment
There was a problem hiding this comment.
I'm fine with what we're fixing, just wonder if there's an easier way to do this
The OpenAI integration captured the raw request kwargs as span metadata, including the openai SDK's `Omit`/`NotGiven` sentinel objects used as defaults for unset parameters. These were serialized to noisy repr strings such as "<openai.Omit object at 0x...>", making metadata unqueryable and burying the real parameters. Frameworks like PydanticAI forward every chat-completion parameter explicitly, defaulting unset ones to `openai.omit`, which is why this surfaced broadly. Filter both sentinel types out before building metadata, in both `get_metadata_from_kwargs` (chat/completion) and `openai_get_metadata_from_response` (responses API). Sentinel types are resolved lazily and independently to avoid a circular import at patch time and to keep `NotGiven` filtering working on openai<2 (which has no `Omit`).
df1efab to
0bf1ef6
Compare
…303440c7.yaml Co-authored-by: Yun Kim <35776586+Yun-Kim@users.noreply.github.com>
|
This change is marked for backport to 4.10 and it does not conflict with that branch. |
…#18552) ## Overview The OpenAI integration captured the raw chat-completion request `kwargs` as LLM span metadata, including the openai SDK's `Omit` / `NotGiven` sentinel objects used as defaults for unset parameters. These were serialized to noisy repr strings such as `"<openai.Omit object at 0x7f5e35900e90>"` across most of the ~21 metadata keys per span, making the metadata field unqueryable and burying the real parameters. ## Motivation Frameworks like **PydanticAI** forward every chat-completion parameter explicitly, defaulting any the caller didn't set to `openai.omit`. ddtrace snapshots the `kwargs` at the wrapper boundary — upstream of the openai SDK's own sentinel-stripping (`_merge_mappings`) — so the sentinels reach span metadata even though they never reach the provider. The request to OpenAI/Azure was always correct; only the recorded metadata was polluted. ## Change Filter `Omit` / `NotGiven` sentinel values out before building metadata, in both: - `get_metadata_from_kwargs` (chat / completion) - `openai_get_metadata_from_response` (responses API) Sentinel types are resolved **lazily and independently** via a small cached helper. Lazy resolution avoids a circular import while ddtrace is patching openai at import time; independent resolution keeps `NotGiven` filtering working on `openai<2` (which has no `Omit`). On openai-less installs the helper returns an empty tuple and the filter is a no-op. ## Testing - Added regression test `test_chat_completion_filters_openai_sentinel_metadata` — passes a real value (`top_p`) alongside `Omit`/`NotGiven` sentinels and asserts only the real value lands in metadata. Fails without the fix. - Verified across the full openai riot matrix (latest, `<2.0.0`, `~=1.76.2`, `==1.66.0`) — including 1.x, which exercises the `NotGiven`-only path. - Reproduced the customer's exact stack (PydanticAI → Azure OpenAI): metadata drops from 21 keys (~20 sentinels) to only real values. ## Risk Low. The change only removes sentinel placeholder values from metadata; real parameter values are unaffected. Behavior is unchanged for non-openai integrations. Jira: MLOB-7613 Co-authored-by: jessica.gamio <jessica.gamio@datadoghq.com> (cherry picked from commit faf70d1) Co-authored-by: Jessica Gamio <52049720+jessicagamio@users.noreply.github.com>
|
This change is marked for backport to 4.11 and it does not conflict with that branch. |
…#18552) ## Overview The OpenAI integration captured the raw chat-completion request `kwargs` as LLM span metadata, including the openai SDK's `Omit` / `NotGiven` sentinel objects used as defaults for unset parameters. These were serialized to noisy repr strings such as `"<openai.Omit object at 0x7f5e35900e90>"` across most of the ~21 metadata keys per span, making the metadata field unqueryable and burying the real parameters. ## Motivation Frameworks like **PydanticAI** forward every chat-completion parameter explicitly, defaulting any the caller didn't set to `openai.omit`. ddtrace snapshots the `kwargs` at the wrapper boundary — upstream of the openai SDK's own sentinel-stripping (`_merge_mappings`) — so the sentinels reach span metadata even though they never reach the provider. The request to OpenAI/Azure was always correct; only the recorded metadata was polluted. ## Change Filter `Omit` / `NotGiven` sentinel values out before building metadata, in both: - `get_metadata_from_kwargs` (chat / completion) - `openai_get_metadata_from_response` (responses API) Sentinel types are resolved **lazily and independently** via a small cached helper. Lazy resolution avoids a circular import while ddtrace is patching openai at import time; independent resolution keeps `NotGiven` filtering working on `openai<2` (which has no `Omit`). On openai-less installs the helper returns an empty tuple and the filter is a no-op. ## Testing - Added regression test `test_chat_completion_filters_openai_sentinel_metadata` — passes a real value (`top_p`) alongside `Omit`/`NotGiven` sentinels and asserts only the real value lands in metadata. Fails without the fix. - Verified across the full openai riot matrix (latest, `<2.0.0`, `~=1.76.2`, `==1.66.0`) — including 1.x, which exercises the `NotGiven`-only path. - Reproduced the customer's exact stack (PydanticAI → Azure OpenAI): metadata drops from 21 keys (~20 sentinels) to only real values. ## Risk Low. The change only removes sentinel placeholder values from metadata; real parameter values are unaffected. Behavior is unchanged for non-openai integrations. Jira: MLOB-7613 Co-authored-by: jessica.gamio <jessica.gamio@datadoghq.com> (cherry picked from commit faf70d1) Co-authored-by: Jessica Gamio <52049720+jessicagamio@users.noreply.github.com>
Overview
The OpenAI integration captured the raw chat-completion request
kwargsas LLM span metadata, including the openai SDK'sOmit/NotGivensentinel objects used as defaults for unset parameters. These were serialized to noisy repr strings such as"<openai.Omit object at 0x7f5e35900e90>"across most of the ~21 metadata keys per span, making the metadata field unqueryable and burying the real parameters.Motivation
Frameworks like PydanticAI forward every chat-completion parameter explicitly, defaulting any the caller didn't set to
openai.omit. ddtrace snapshots thekwargsat the wrapper boundary — upstream of the openai SDK's own sentinel-stripping (_merge_mappings) — so the sentinels reach span metadata even though they never reach the provider. The request to OpenAI/Azure was always correct; only the recorded metadata was polluted.Change
Filter
Omit/NotGivensentinel values out before building metadata, in both:get_metadata_from_kwargs(chat / completion)openai_get_metadata_from_response(responses API)Sentinel types are resolved lazily and independently via a small cached helper. Lazy resolution avoids a circular import while ddtrace is patching openai at import time; independent resolution keeps
NotGivenfiltering working onopenai<2(which has noOmit). On openai-less installs the helper returns an empty tuple and the filter is a no-op.Testing
test_chat_completion_filters_openai_sentinel_metadata— passes a real value (top_p) alongsideOmit/NotGivensentinels and asserts only the real value lands in metadata. Fails without the fix.<2.0.0,~=1.76.2,==1.66.0) — including 1.x, which exercises theNotGiven-only path.Risk
Low. The change only removes sentinel placeholder values from metadata; real parameter values are unaffected. Behavior is unchanged for non-openai integrations.
Jira: MLOB-7613